package log

import (
	"fmt"
	"log"
	"runtime"
	"strings"
	"sync"
	"time"
)

var (
	LEVEL_FLAGS = [...]string{"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
)

const (
	TRACE = iota
	DEBUG
	INFO
	WARNING
	ERROR
	FATAL
)

const (
	tunnelBufferSize = 1024
	maxFlushInterval = 1000   // 单位毫秒
	rotateInterval   = 864000 // 单位秒
)

type Record struct {
	time string
	// 文件名:行号
	code  string
	info  string
	level int
}

func (r *Record) String() string {
	return fmt.Sprintf("[%s][%s][%s] %s\n", LEVEL_FLAGS[r.level], r.time, r.info, r.code)
}

type Logger struct {
	// 多个输出位置
	writers []Writer
	// 存放批量提交的 Record 管道
	tunnel     chan *Record
	recordPool *sync.Pool

	// 日志开关级别
	level       int
	lastTime    int64
	lastTimeStr string
	// 时间格式
	layout string
	// 是否记录调用栈
	tracing bool
}

func NewLogger() *Logger {
	l := new(Logger)
	l.writers = []Writer{}
	l.tunnel = make(chan *Record, tunnelBufferSize)
	l.level = DEBUG
	l.layout = "2006/01/02 15:04:05"
	l.recordPool = &sync.Pool{New: func() interface{} {
		return &Record{}
	}}

	// 启动消费者：写缓存、定时刷盘和 rotate
	go l.startLoop()

	return l
}

func (l *Logger) Register(w Writer) {
	if err := w.Init(); err != nil {
		panic(err)
	}
	l.writers = append(l.writers, w)
}

func (l *Logger) SetLevel(lvl int) {
	l.level = lvl
}

func (l *Logger) SetLayout(layout string) {
	l.layout = layout
}

func (l *Logger) SetTracing(tracing bool) {
	l.tracing = tracing
}

func (l *Logger) Trace(fmt string, args ...interface{}) {
	l.addRecord(TRACE, fmt, args...)
}

func (l *Logger) Debug(fmt string, args ...interface{}) {
	l.addRecord(DEBUG, fmt, args...)
}

func (l *Logger) Warn(fmt string, args ...interface{}) {
	l.addRecord(WARNING, fmt, args...)
}

func (l *Logger) Info(fmt string, args ...interface{}) {
	l.addRecord(INFO, fmt, args...)
}

func (l *Logger) Error(fmt string, args ...interface{}) {
	l.addRecord(ERROR, fmt, args...)
}

func (l *Logger) Fatal(fmt string, args ...interface{}) {
	l.addRecord(FATAL, fmt, args...)
}

func (l *Logger) Close() {
	close(l.tunnel)
}

func (l *Logger) addRecord(level int, format string, args ...interface{}) {
	var inf string

	if level < l.level {
		return
	}

	if format != "" {
		inf = fmt.Sprintf(format, args...)
	} else {
		inf = fmt.Sprint(args...)
	}

	// format time
	now := time.Now()
	if now.Unix() != l.lastTime {
		l.lastTime = now.Unix()
		l.lastTimeStr = now.Format(l.layout)
	}
	r := l.recordPool.Get().(*Record)
	r.info = inf
	if l.tracing {
		r.code = callersFormat()
	}

	r.time = l.lastTimeStr
	r.level = level

	l.tunnel <- r
}

func callersFormat() string {
	stk := make([]uintptr, 32)
	n := runtime.Callers(5, stk[:])
	stk = stk[:n]
	frames := runtime.CallersFrames(stk)
	w := &strings.Builder{}
	for {
		frame, more := frames.Next()
		// if strings.Contains(frame.File, "/pkg/mod/") || strings.Contains(frame.File, "/src/runtime/") {
		// 	continue
		// }
		fmt.Fprintf(w, "%s:%d", frame.File, frame.Line)
		if !more {
			break
		} else {
			w.WriteString(",")
		}
	}
	return w.String()
}

func (l *Logger) Write(r *Record) {
	for _, w := range l.writers {
		if err := w.Write(r); err != nil {
			log.Println(err)
		}
	}
}

func (l *Logger) Flush() {
	for _, w := range l.writers {
		if f, ok := w.(Flusher); ok {
			if err := f.Flush(); err != nil {
				log.Println(err)
			}
		}
	}
}

func (l *Logger) Rotate() {
	for _, w := range l.writers {
		if r, ok := w.(Rotater); ok {
			if err := r.Rotate(); err != nil {
				log.Println(err)
			}
		}
	}
}

func (l *Logger) startLoop() {
	flushTimer := time.NewTimer(time.Millisecond * maxFlushInterval)
	rotateTimer := time.NewTimer(time.Second * rotateInterval)

	for {
		select {
		case r, ok := <-l.tunnel:
			if !ok {
				l.Flush()
				return
			}
			// 先写缓存
			l.Write(r)
			// 放回池中
			l.recordPool.Put(r)
		case <-flushTimer.C: // 定时刷盘
			l.Flush()
			flushTimer.Reset(time.Millisecond * maxFlushInterval)
		case <-rotateTimer.C: // 处理 rotate
			l.Rotate()
			rotateTimer.Reset(time.Second * rotateInterval)
		}
	}
}

// default logger
var (
	logger_default *Logger
	once           sync.Once
)

func defaultLoggerInit() {
	once.Do(func() {
		logger_default = NewLogger()
	})
}

func SetLevel(lvl int) {
	defaultLoggerInit()
	logger_default.level = lvl
}

func SetLayout(layout string) {
	defaultLoggerInit()
	logger_default.layout = layout
}

func Trace(fmt string, args ...interface{}) {
	defaultLoggerInit()
	logger_default.addRecord(TRACE, fmt, args...)
}

func Debug(fmt string, args ...interface{}) {
	defaultLoggerInit()
	logger_default.addRecord(DEBUG, fmt, args...)
}

func Warn(fmt string, args ...interface{}) {
	defaultLoggerInit()
	logger_default.addRecord(WARNING, fmt, args...)
}

func Info(fmt string, args ...interface{}) {
	defaultLoggerInit()
	logger_default.addRecord(INFO, fmt, args...)
}

func Error(fmt string, args ...interface{}) {
	defaultLoggerInit()
	logger_default.addRecord(ERROR, fmt, args...)
}

func Fatal(fmt string, args ...interface{}) {
	log.Fatalf(fmt, args...)
	// defaultLoggerInit()
	// logger_default.addRecord(FATAL, fmt, args...)
}

func Register(w Writer) {
	defaultLoggerInit()
	logger_default.Register(w)
}

func Close() {
	defaultLoggerInit()
	logger_default.Close()
	logger_default = nil
}
